/*
 * Copyright (c) 2022, IMMORTA Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * - Redistributions of source code must retain the above copyright notice, this list
 *   of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright notice, this
 *   list of conditions and the following disclaimer in the documentation and/or
 *   other materials provided with the distribution.
 *
 * - Neither the name of IMMORTA Inc. nor the names of its
 *   contributors may be used to endorse or promote products derived from this
 *   software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef UART_DRV_H
#define UART_DRV_H

#include <stdbool.h>
#include "errcodes.h"
#include "device_registers.h"
#include "os_adapter.h"
/*!
 * @file uart_drv.h
 * @brief This file declares uart driver interfaces
 */

/*!
 * @addtogroup uart_drv
 * @{
 */

/*******Includes***************************************************************/

/*******Definitions************************************************************/
/*!
 * @brief UART number of bits in a character
 */
typedef enum {
    UART_WORD_LEN_7BIT  = 0x00U,             /*!< 7-bit data characters */
    UART_WORD_LEN_8BIT  = 0x01U,             /*!< 8-bit data characters */
    UART_WORD_LEN_9BIT  = 0x02U              /*!< 9-bit data characters */
} uart_word_length_t;

/*!
 *  @brief UART parity mode
 */
typedef enum {
    UART_PARITY_DISABLED = 0x00U,            /*!< Parity disabled, bit setting: PEN = 0 */
    UART_PARITY_ODD      = 0x02U,            /*!< Parity enabled, type odd, bit setting: PEN|PTYPE = 10 */
    UART_PARITY_EVEN     = 0x03U             /*!< Parity enabled, type even,  bit setting: PEN|PTYPE = 11 */
} uart_parity_mode_t;

/*!
 *  @brief UART number of stop bits
 */
typedef enum {
    UART_ONE_STOP_BIT = 0x00U,              /*!< One stop bit */
    UART_TWO_STOP_BIT = 0x01U               /*!< Two stop bits */
} uart_stop_bit_count_t;

/*!
 *  @brief UART baud over-sample rate
 */
typedef enum {
    UART_OVERSAMPLE_16 = 0x00U,             /*!< 16x oversampling, baud = fclk/[(BRINT+BRDEC>>5) * 16] */
    UART_OVERSAMPLE_4  = 0x01U              /*!< 4x oversampling   baud = fclk/[(BRINT+BRDEC>>5) * 4]  */
} uart_oversample_rate_t;

/*!
 *  @brief Type of UART transfer (based on interrupts or DMA)
 */
typedef enum {
    UART_USING_INTERRUPT    = 0x00U,        /*!< The driver will use interrupts to perform UART transfer */
    UART_USING_DMA          = 0x01U         /*!< The driver will use DMA to perform UART transfer */
} uart_transfer_type_t;

/*!
 *  @brief Define the enum of the events which can trigger UART callback
 */
typedef enum {
    UART_EVENT_RX_COMPLETE  = 0x00U,        /*!< Rx receive complete */
    UART_EVENT_TX_EMPTY     = 0x01U,        /*!< Tx buffer is empty */
    UART_EVENT_END_TRANSFER = 0x02U,        /*!< The current transfer is ending */
    UART_EVENT_ERROR        = 0x03U,        /*!< An error occured during transfer */
    UART_EVENT_RX_IDLE      = 0x04U         /*!< Rx receive is idle */
} uart_event_t;

/*!
 * @brief Callback for all peripherals which support UART features
 */
typedef void (*uart_callback_t)(uint32_t instance, uart_event_t event, void* param);
/*!
 * @brief Callback for all peripherals which support UART features
 */
typedef void (*uart_irqhandler_process_t)(uint32_t instance);

/*!
 * @brief Uart driver configuration structure
 */
typedef struct {
    uint32_t baudRate;                       /*!< UART baud rate */
    uart_oversample_rate_t oversampleRate;   /*!< UART baud over-sample rate */
    uart_word_length_t wordLength;           /*!< number of bits in a character (7,8-default,9) */
    uart_parity_mode_t parityMode;           /*!< parity mode, disabled (default), even, odd */
    uart_stop_bit_count_t stopBitCount;      /*!< number of stop bits, 1 stop bit (default) or 2 stop bits */
    uart_transfer_type_t transferType;       /*!< Type of UART transfer (interrupt/dma based) */
    bool rxIdleIntEnable;                    /*!< True if enable the idle interrupt */
    uint8_t rxDMAChannel;                    /*!< Channel number for DMA rx channel */
    uint8_t txDMAChannel;                    /*!< Channel number for DMA tx channel */

    uart_callback_t callback;                /*!< Uart callback function */
    void* callbackParam;                     /*!< Uart callback parameters if necessary */
} uart_config_t;

/*!
 * @brief Uart driver configuration structure
 */
typedef struct {
    const uint8_t* txBuff;                   /*!< The buffer of data being sent */
    uint8_t* rxBuff;                         /*!< The buffer of received data */
    volatile uint32_t txSize;                /*!< The remaining number of bytes to be transmitted */
    volatile uint32_t rxSize;                /*!< The remaining number of bytes to be received */
    volatile bool isTxBusy;                  /*!< True if there is an active transmit */
    volatile bool isRxBusy;                  /*!< True if there is an active receive */
    volatile bool isTxBlocking;              /*!< True if transmit is blocking transaction */
    volatile bool isRxBlocking;              /*!< True if receive is blocking transaction */
    semaphore_t rxComplete;                  /*!< Synchronization object for blocking Rx timeout condition */
    semaphore_t txComplete;                  /*!< Synchronization object for blocking Tx timeout condition */
    volatile errcode_t transmitStatus;       /*!< Status of last driver transmit operation */
    volatile errcode_t receiveStatus;        /*!< Status of last driver receive operation */

    uint8_t txDMAChannel;                    /*!< DMA channel number for DMA-based tx */
    uint8_t rxDMAChannel;                    /*!< DMA channel number for DMA-based rx */
    uart_oversample_rate_t oversampleRate;   /*!< UART baud over-sample rate */
    uart_word_length_t wordLength;           /*!< number of bits in a character (7,8-default,9) */
    uart_transfer_type_t transferType;       /*!< Type of LPUART transfer (interrupt/dma based) */
    uart_callback_t callback;                /*!< Uart callback function */
    void* callbackParam;                     /*!< Callback parameter pointer */
} uart_state_t;


/*******APIs*******************************************************************/
#if defined(__cplusplus)
extern "C" {
#endif

/*!
 * @brief Get the UART configuration structure with default values
 *
 * @param[out] uartConfig: Uart configuration structure
 * @return None
 */
void UART_GetDefaultConfig(uart_config_t* uartConfig);


/*!
 * @brief Initializes an UART operation instance
 *
 * @param[in] instance: UART instance number
 * @param[out] uartState: Save the parameters and status of the uart, this point will be reserved
                          for asynchronously use, it must be pointed to static or global memory
 * @param[in] uartConfig: Uart configuration structure
 * @return ERR_SUCCESS: Data send successfully
 *         ERR_ERROR: An error occurred
 */
errcode_t UART_Init(uint32_t instance,
                    uart_state_t* uartState,
                    const uart_config_t* uartConfig);

/*!
 * @brief Deinitializes an UART operation instance
 *
 * @param[in] instance: UART instance number
 * @return None
 */
void UART_Deinit(uint32_t instance);


/*!
 * @brief Registers the callback function and the parameter for uart instance
 *
 * @param[in] instance: UART instance number
 * @param[in] callback: The pointer to the callback function
 * @param[in] param: The pointer to the callback function's parameter
 * @return None
 */
void UART_InstallCallback(uint32_t instance,
                          uart_callback_t callback,
                          void* param);

/*!
 * @brief Send data in a blocking way with a set timeout
 *
 * @param[in] instance: UART instance number
 * @param[in] txBuff: Data buffer to send
 * @param[in] txSize: Size of data buffer to send
 * @param[in] timeout: Maximum timeout to wait
 * @return ERR_SUCCESS: Data send successfully
 *         ERR_ERROR: An error occurred
 *         ERR_TIMEOUT: Data send timeout
 *         ERR_BUSY: If the resource is busy
 */
errcode_t UART_SendDataBlocking(uint32_t instance,
                                const uint8_t* txBuff,
                                uint32_t txSize,
                                uint32_t timeout);

/*!
 * @brief Send data in polling way, wait for tx to be empty and fill in data
 *
 * @param[in] instance: UART instance number
 * @param[in] txBuff: Data buffer to send
 * @param[in] txSize: Size of data buffer to send
 * @return ERR_SUCCESS: Data send successfully
 *         ERR_ERROR: An error occurred
 *         ERR_BUSY: If the resource is busy
 */
errcode_t UART_SendDataPolling(uint32_t instance,
                               const uint8_t* txBuff,
                               uint32_t txSize);

/*!
 * @brief Send data in a non-blocking way, the function returns immediately
 *
 * @param[in] instance: UART instance number
 * @param[in] txBuff: Data buffer to send
 * @param[in] txSize: Size of data buffer to send
 * @return ERR_SUCCESS: Data send successfully
 *         ERR_BUSY: If the resource is busy
 */
errcode_t UART_SendData(uint32_t instance,
                        const uint8_t* txBuff,
                        uint32_t txSize);

/*!
 * @brief Abort sending data
 *
 * @param[in] instance: UART instance number
 * @return None
 */
void UART_AbortSendingData(uint32_t instance);

/*!
 * @brief Get the current transfer status
 *
 * @param[in] instance: UART instance number
 * @param[out] bytesRemaining: Current remaining amount of data transfer
 * @return ERR_SUCCESS: The transmit has completed successfully
 *         ERR_BUSY: The transmit is still in progress
 *         ERR_UART_ABORTED: Transfer was aborted
 *         ERR_TIMEOUT: Data send timeout
 *         ERR_ERROR: An error occurred
 */
errcode_t UART_GetTransmitStatus(uint32_t instance, uint32_t* bytesRemaining);

/*!
 * @brief Receive data in a blocking way with a set timeout
 *
 * @param[in] instance: UART instance number
 * @param[in] rxBuff: Data buffer to receive
 * @param[in] rxSize: Size of data buffer to receive
 * @param[in] timeout: Maximum timeout to wait
 * @return ERR_SUCCESS: Data receive successfully
 *         ERR_ERROR: An error occurred
 *         ERR_TIMEOUT: Data receive timeout
 *         ERR_BUSY: If the resource is busy
 *         ERR_UART_RX_OVERRUN: Rx overrun error
 *         ERR_UART_FRAMING_ERROR: Framing error
 *         ERR_UART_PARITY_ERROR:  Parity error
 *         ERR_UART_NOISE_ERROR: Noise error
 */
errcode_t UART_ReceiveDataBlocking(uint32_t instance,
                                   uint8_t* rxBuff,
                                   uint32_t rxSize,
                                   uint32_t timeout);

/*!
 * @brief Receive data in polling way, wait for rx to be non-empty and fill in data
 *
 * @param[in] instance: UART instance number
 * @param[in] rxBuff: Data buffer to receive
 * @param[in] rxSize: Size of data buffer to receive
 * @return ERR_SUCCESS: Data receive successfully
 *         ERR_ERROR: Data receive fail
 *         ERR_BUSY: If the resource is busy
 *         ERR_UART_RX_OVERRUN: Rx overrun error
 *         ERR_UART_FRAMING_ERROR: Framing error
 *         ERR_UART_PARITY_ERROR:  Parity error
 *         ERR_UART_NOISE_ERROR: Noise error
 */
errcode_t UART_ReceiveDataPolling(uint32_t instance,
                                  uint8_t* rxBuff,
                                  uint32_t rxSize);

/*!
 * @brief Receive data in a non-blocking way, the function returns immediately
 *
 * @param[in] instance: UART instance number
 * @param[in] rxBuff: Data buffer to receive
 * @param[in] rxSize: Size of data buffer to receive
 * @return ERR_SUCCESS: Data receive successfully
 *         ERR_BUSY: If the resource is busy
 */
errcode_t UART_ReceiveData(uint32_t instance,
                           uint8_t* rxBuff,
                           uint32_t rxSize);

/*!
 * @brief Receive data in a cyclical way by using DMA loop mode, the function returns immediately
 *
 * @param[in] instance: UART instance number
 * @param[in] rxBuff: Data buffer to receive, the buffer size should exceed the burst length of data
 * @param[in] rxSize: Size of data buffer to receive
 * @return ERR_SUCCESS: Data receive successfully
 *         ERR_BUSY: If the resource is busy
 */
errcode_t UART_ReceiveDataCyclically(uint32_t instance,
                                     uint8_t* rxBuff,
                                     uint32_t rxSize);

/*!
 * @brief Get the current receive status
 *
 * @param[in] instance: UART instance number
 * @param[out] bytesRemaining: The current remaining amount of received data
 * @return ERR_SUCCESS: All data received successfully
 *         ERR_BUSY: Data reception is already in progress
 *         ERR_UART_ABORTED: Receiving data is aborted
 *         ERR_TIMEOUT: Data receive timeout
 *         ERR_ERROR: An error occurred
 *         ERR_UART_RX_OVERRUN: Rx overrun error
 *         ERR_UART_FRAMING_ERROR: Framing error
 *         ERR_UART_PARITY_ERROR:  Parity error
 *         ERR_UART_NOISE_ERROR: Noise error]
 * Note: When the return is ERR_SUCCESS, the value of byteRemaining may be 1.
   This is because after bytesRemaining is assigned, it may enter the interrupt,
   and the value of receiveStatus becomes ERR_SUCCESS, so return value is ERR_SUCCESS.
 */
errcode_t UART_GetReceiveStatus(uint32_t instance, uint32_t* bytesRemaining);

/*!
 * @brief Abort receiving data
 *
 * @param[in] instance: UART instance number
 * @return None
 */
void UART_AbortReceivingData(uint32_t instance);

/*!
 * @brief Configures the uart baud rate
 *
 * @param[in] instance: UART instance number.
 * @param[in] baudRate: UART desired baud rate.
 * @return ERR_BUSY: if called during an on-going transfer
 *         ERR_SUCCESS: Configures the uart baud rate successfully
 */
errcode_t UART_SetBaudRate(uint32_t instance, uint32_t baudRate);

/*!
 * @brief Get the uart baud rate
 *
 * @param[in] instance: UART instance number.
 * @param[out] baudRate: UART baud rate.
 * @return None
 */
void UART_GetBaudRate(uint32_t instance, uint32_t* baudRate);

/*!
 * @brief UART interrupt handler process
 *
 * @param[in] instance: UART instance number.
 * @return None
 */
void UART_IRQHandlerProcess(uint32_t instance);

/*!
 * @brief Registers the irq handler process function for uart instance
 *
 * @param[in] instance: UART instance number
 * @param[in] callback: The pointer to the irq handler process function
 * @return None
 */
void UART_InstallIRQHandlerProcess(uint32_t instance, uart_irqhandler_process_t irqHandlerProcess);

#if defined(__cplusplus)
}
#endif

/*! @} */

#endif /* UART_DRV_H */

/*******EOF********************************************************************/

